package com.example.rpc.demo.netty;

import com.example.rpc.demo.annotation.RpcService;
import com.example.rpc.demo.registry.ServiceRegistry;
import com.example.rpc.demo.rpc.RpcRequest;
import com.example.rpc.demo.rpc.RpcResponse;
import com.example.rpc.demo.utils.ServicePathUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;


@Component
public class RpcServer implements ApplicationContextAware, InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(RpcServer.class);

    @Value("${rpc.server.address}")
    private String serverAddress;

    @Autowired
    private ServiceRegistry serviceRegistry;

    private Map<String, Object> handlerMap = new HashMap<>();


    @Override
    public void afterPropertiesSet() throws Exception {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new RpcMessageDecoder(RpcRequest.class));
                    ch.pipeline().addLast(new RpcMessageEncoder(RpcResponse.class));
                    ch.pipeline().addLast(new RpcInvokeHandler(handlerMap));
                }
            });
            bootstrap.option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            String[] array = serverAddress.split(":");
            String host = array[0];
            int port = Integer.parseInt(array[1]);

            ChannelFuture future = bootstrap.bind(host,port).sync();

            logger.info("rpc server started on port {}", port);

            if(serviceRegistry !=null) {
                handlerMap.forEach((key,value)->{serviceRegistry.registry(key,serverAddress);});
            }

            future.channel().closeFuture().sync();



        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String,Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if(serviceBeanMap!=null) {
            for (Object serviceBean : serviceBeanMap.values()) {
                Class<?> interfaceClass = serviceBean.getClass().getAnnotation(RpcService.class).value();
                String interfaceName = ServicePathUtil.getFullName(interfaceClass);
                handlerMap.put(interfaceName, serviceBean);
            }
        }
    }
}
